#include"test.h"
#define BUFFER_SIZE 8//缓冲区大小
//通过位图的方式，控制刷新方式
#define BUFFER_NONE 0X1 //无缓冲
#define BUFFER_LINE 0x2 //行缓冲
#define BUFFER_ALL 0X4  //全缓冲

typedef struct MY_FILE
{
  char _buffer[BUFFER_SIZE];//缓冲区
  size_t _current;          //缓冲区下标
  int _flush;               //刷新方式:通过位图的方式改变

  int _fd;                  //文件描述符

}MY_FILE;

//打开文件
MY_FILE *myopen(const char *path,const char *mode)
{
  assert(path && mode);
  int flags=0;
  if(*mode == 'r')
  {
    flags |=O_RDONLY;
    if(strcmp("r+",mode)==0)
      flags |=O_WRONLY;
  }
     else if (*mode == 'w' || *mode == 'a')
    {
        flags |= (O_WRONLY | O_CREAT);

        if (*mode == 'w')
            flags |= O_TRUNC;
        else
            flags |= O_APPEND;

        if (strcmp("w+", mode) == 0 || strcmp("a+", mode) == 0)
            flags |= O_RDONLY;
    }
    else
    {
        // 无效打开方式
        assert(flags);
    }

    // 根据打开方式，打开文件
    // 注意新建文件需要设置权限
    int fd = 0;
    if (flags & O_CREAT)
        fd = open(path, flags, 0666);
    else
        fd = open(path, flags);

    if (fd == -1)
    {
        // 打开失败的情况
        return NULL;
    }

    // 打开成功了，创建 MY_FILE 结构体，并返回
    MY_FILE *new_file = (MY_FILE *)malloc(sizeof(MY_FILE));
    if (new_file == NULL)
    {
        // 此处不能断言，需要返回空
        close(fd); // 需要先把 fd 关闭
        perror("malloc FILE fail!");
        return NULL;
    }

    // 初始化 MY_FILE
    memset(new_file->_buffer, '\0', BUFFER_SIZE); // 初始化缓冲区
    new_file->_current = 0;                       // 下标置0
    new_file->_flush = BUFFER_LINE;               // 行刷新
    new_file->_fd = fd;                           // 设置文件描述符

    return new_file;
}
// 缓冲区刷新
int my_fflush(MY_FILE *stream)
{
    assert(stream);

    // 将数据写给文件
    int ret = write(stream->_fd, stream->_buffer, stream->_current);
    stream->_current = 0; // 每次刷新后，都需要清空缓冲区
    fsync(stream->_fd);   // 将内核中的数据强制刷给磁盘(文件)

    if (ret != -1) return 0;
    else return -1;
}

int my_fclose(MY_FILE *fp)
{
    assert(fp);

    // 刷新残余数据
    if (fp->_current > 0)
        my_fflush(fp);

    // 关闭 fd
    int ret = close(fp->_fd);

    // 释放已开辟的空间
    free(fp);
    fp = NULL;

    return ret;
}
// 数据写入
size_t my_fwrite(const void *ptr, size_t size, size_t nmemb, MY_FILE *stream)
{
    // 写入先判断缓冲区是否已满
    if (stream->_current == BUFFER_SIZE)
        my_fflush(stream);

    size_t user_size = size * nmemb;                 // 用户想写入的字节数
    size_t my_size = BUFFER_SIZE - stream->_current; // 缓冲区中剩余可用空间
    size_t writen = 0;                               // 成功写入数据的大小

    if (my_size >= user_size)
    {
        // 直接可用全部写入
        memcpy(stream->_buffer + stream->_current, ptr, user_size);
        stream->_current += user_size;
        writen = user_size;
    }
    else
    {
        // 一次写不完，需要分批写入
        size_t tmp = user_size; // 用于定位 ptr 的读取位置
        while (user_size > my_size)
        {
            // 一次写入 my_size 个数据。user_size 会减小
            memcpy(stream->_buffer + stream->_current, ptr + (tmp - user_size), my_size);
            stream->_current += my_size; // 切记实时更新下标
            my_fflush(stream);           // 写入后，刷新缓冲区
            user_size -= my_size;
            my_size = BUFFER_SIZE - stream->_current;
        }
        // 最后空间肯定足够，再把数据写入缓冲区中
        memcpy(stream->_buffer + stream->_current, ptr + (tmp - user_size), user_size);
        stream->_current += user_size;
        writen = tmp;
    }

    // 通过刷新方式，判断是否进行刷新
    if (stream->_flush & BUFFER_NONE)
    {
        // 无缓冲，直接冲刷
        my_fflush(stream);
    }
    else if (stream->_flush & BUFFER_LINE)
    {
        // 行缓冲，遇见 '\n' 才刷新
        if (stream->_buffer[stream->_current - 1] == '\n')
            my_fflush(stream);
    }
    else
    {
        // 全缓冲，满了才刷新
        if (stream->_current == BUFFER_SIZE)
            my_fflush(stream);
    }

    // 为了简化，这里返回用户实际写入的字节数，即 user_size
    return writen;
}
// 数据读取
size_t my_fread(void *ptr, size_t size, size_t nmemb, MY_FILE *stream)
{
    // 数据读取前，需要先把缓冲区刷新
    if (stream->_current > 0)
        my_fflush(stream);

    size_t user_size = size * nmemb;
    size_t my_size = BUFFER_SIZE;

    // 先将数据读取到FILE缓冲区中，再赋给 ptr
    if (my_size >= user_size)
    {
        // 此时缓冲区中足够存储用户需要的所有数据，只需要读取一次
        read(stream->_fd, stream->_buffer, my_size);
        memcpy(ptr, stream->_buffer, my_size);
        *((char *)ptr + my_size - 1) = '\0';
    }
    else
    {
        int ret = 1;
        size_t tmp = user_size;
        while (ret)
        {
            // 一次读不完，需要多读取几次
            ret = read(stream->_fd, stream->_buffer, my_size);
            memcpy(ptr + (tmp - user_size), stream->_buffer, ret);
            stream->_current = 0;
            user_size -= my_size;
        }
        *((char *)ptr + tmp) = '\0';
    }

    size_t readn = strlen(ptr);

    return readn;
}





//测试
int main()
{
    //打开文件，写入一段话
    MY_FILE* fp = myopen("log.txt", "w+");
    assert(fp);

    char inPutBuff[512] = "2110年1月1日，距离地球能源完全枯竭还有3650天。为了解决地球能源危机，\n人类制造了赛尔机器人和宇宙能源探索飞船赛尔号，去寻找神秘精灵看守的无尽能源。";

    size_t n = my_fwrite(inPutBuff, 1, strlen(inPutBuff), fp);
    printf("本次成功写入 %d 字节的数据", n);

    my_fclose(fp);

    printf("\n==============================\n");

    //重新打开文件
    fp = myopen("log.txt", "r");
    assert(fp);

    char outPutBuff[512] = { '\0' };
    n = my_fread(outPutBuff, 1, sizeof(outPutBuff), fp);
    printf("本次成功读取 %d 字节的数据，具体内容为: \n%s\n", n, outPutBuff);

    my_fclose(fp);
    fp = NULL;
    return 0;
}







